#include "particle.h"
#define SQR(x) ( (x)*(x) )

#define ITER_LIST( iter,_list ) \
   for(iter=_list->begin(); iter!=_list->end(); ++iter)

void Particle::addConstraint  ( Constraint & _constraint  )  {
   pDebug( this << " void Particle::addConstraint (" << "Constraint & _constraint =" << &_constraint   << ")")
    vel.show();
   constraints->push_back( &_constraint );
}

void Particle::honorConstraints (  )  {
   pDebug( this << " void Particle::honorConstraints("   << ")")
    vel.show();
   Constraint* constraint;
   while( ! constraints->empty() ) {
      constraint = constraints->front();
      constraints->pop_front();
      (*constraint).honor( this );
   }
}

void ParticleList::add  ( Particle * particle  )  {
   pDebug( this << " void ParticleList::add (" << "Particle * particle =" << &particle   << ")")
    List::add ( (Listable*) particle );
}

Particle * ParticleList::iter  (  )  {
   pDebug( this << " Particle * ParticleList::iter ("   << ")")
    return ( Particle* ) List::listIter ();
}

void ParticleEngine::add  ( Particle & particle  )  {
   pDebug( this << " void ParticleEngine::add (" << "Particle & particle =" << &particle   << ")")
    particles.push_back ( particle );
   drawableList->add ( particle.getDrawable() );
}

void ParticleEngine::applyFields () {
   std::list<Particle>::iterator particle;
   std::list<Field>::iterator field;
   for(particle=particles.begin(); particle != particles.end(); ++particle) {
      particle->resetAcc();
      for(field=fields.begin(); field != fields.end(); ++field) field->fap( &(*particle), pc );
   }
}

void ParticleEngine::actualize  (  )  {
   pDebug( this << " void ParticleEngine::actualize ("   << ")")
    std::list<Particle>::iterator particle;
   UINT ticks = pc->timer->getTicks();
   applyFields();
   checkCollisions();
   for(particle=particles.begin(); particle != particles.end(); ++particle) {
      particle->honorConstraints();

      particle->vel += ( particle->acc * pc->timeResulution );
      particle->pos += ( particle->vel * pc->timeResulution );

      particle->actualizeSprite( ticks );
   }
}

void ParticleEngine::addField  ( Field & _field  )  {
   pDebug( this << " void ParticleEngine::addField (" << "Field & _field =" << &_field   << ")")
    fields.push_back ( _field );
}

void ParticleEngine::checkCollisions() {
   std::list<Particle>::iterator a;
   std::list<Particle>::iterator b;

   for ( a=particles.begin(); a!=particles.end(); ++a) {
      for ( b=a; b!=particles.end(); ++b) {
	 if ( checkRectCollision(
		  *(a->getDrawable()->getPosRect()), 
		  *(b->getDrawable()->getPosRect()) ) == 1 ) {
	    pDebug("Collision Detected");
	 }
      }
   }
}

#define MAX(a,b) ( (a>b)?a:b )

//returns 1 if collision 0 otherwise
int checkRectCollision( Rect& a, Rect &b) {
   if (&a==&b) return 0;
   UINT minW = MAX(a.w,b.w); 
   UINT minH;
   UINT difx = abs(a.x-b.x);
   UINT dify;
   int ret = 0;

   if (difx < minW ) {
      dify = abs(a.y-b.y);
      minH = MAX(a.h,b.h); 
      if (dify < minH) {
	 ret = 1;
      }
   }
   return ret;
}

void PlaneConstraint::honor ( Particle *p ) {
   // I'll code the general case at once if the particle has speed in the
   // direction of the normal towards the plane then such speed will become null
   // if the particle is sticky or reversed otherwise
   
   //get the component of the acc in the direction of the normal

   float accProjection = p->acc*normal;
   float velProjection = p->vel*normal;

   pDebug("Updating plane constraint: "<< accProjection << " " << velProjection );

   Vector acc = normal*accProjection;
   if (accProjection<0.)
      //we substract that acc from the particle
      p->acc -= acc;

   //Now the velocity
   Vector vel = normal*velProjection;
   if (velProjection<0.) {
      if ( p->pd->properties & PARTICLE_IS_UNELASTIC ) {
	 //the velocity in the direction will become cero
	 p->vel-=vel;
      } else {
	 p->vel-=vel*2.;
      }
   }
}
